package io.kiki.stack.netty;

import io.kiki.stack.netty.channelManager.*;
import io.kiki.stack.netty.config.Config;
import io.kiki.stack.netty.exception.RemotingException;
import io.kiki.stack.netty.invoker.Callback;
import io.kiki.stack.netty.invoker.RequestContext;
import io.kiki.stack.netty.invoker.RpcResponseFuture;
import io.kiki.stack.netty.lifeCycle.LifeCycle;
import io.kiki.stack.netty.userProcessor.UserProcessor;
import io.kiki.stack.netty.userProcessor.UserProcessorRegisterHelper;

import java.util.List;
import java.util.Map;

public interface IClient extends Config, LifeCycle {

    /**
     * One way invocation using a string address, address format example - 127.0.0.1:12200?key1=value1&key2=value2 <br>
     * <p>
     * Notice:<br>
     * <ol>
     * <li><b>DO NOT modify the request object concurrently when this method is called.</b></li>
     * <li>When do invocation, use the string address to find a available Channel, if none then create one.</li>
     *    <ul>
     *      <li>You can use {@link RpcConfigs#Channel_TIMEOUT_KEY} to specify Channel timeout, time unit is milliseconds, e.g [127.0.0.1:12200?_CONNECTTIMEOUT=3000]
     *      <li>You can use {@link RpcConfigs#Channel_NUM_KEY} to specify channel number for each ip and port, e.g [127.0.0.1:12200?_channelNUM=30]
     *      <li>You can use {@link RpcConfigs#Channel_WARMUP_KEY} to specify whether need warmup all channels for the first time you call this method, e.g [127.0.0.1:12200?_channelWARMUP=false]
     *    </ul>
     * <li>You should use {@link #closeChannel(String addr)} to close it if you want.
     * </ol>
     */
    void oneway(final String address, final Object request) throws RemotingException, InterruptedException;


    void oneway(final String address, final Object request, final RequestContext requestContext) throws RemotingException, InterruptedException;


    void oneway(final Url url, final Object request) throws RemotingException, InterruptedException;


    void oneway(final Url url, final Object request, final RequestContext requestContext) throws RemotingException, InterruptedException;

    void oneway(final Channel channel, final Object request) throws RemotingException;


    void oneway(final Channel channel, final Object request, final RequestContext requestContext) throws RemotingException;

    /**
     * Synchronous invocation using a string address, address format example - 127.0.0.1:12200?key1=value1&key2=value2 <br>
     * <p>
     * Notice:<br>
     * <ol>
     * <li><b>DO NOT modify the request object concurrently when this method is called.</b></li>
     * <li>When do invocation, use the string address to find a available channel, if none then create one.</li>
     *    <ul>
     *      <li>You can use {@link RpcConfigs#Channel_TIMEOUT_KEY} to specify channel timeout, time unit is milliseconds, e.g [127.0.0.1:12200?_CONNECTTIMEOUT=3000]
     *      <li>You can use {@link RpcConfigs#Channel_NUM_KEY} to specify channel number for each ip and port, e.g [127.0.0.1:12200?_channelNUM=30]
     *      <li>You can use {@link RpcConfigs#Channel_WARMUP_KEY} to specify whether need warmup all channels for the first time you call this method, e.g [127.0.0.1:12200?_channelWARMUP=false]
     *    </ul>
     * <li>You should use {@link #closeChannel(String addr)} to close it if you want.
     * </ol>
     */
    Object invokeSync(final String address, final Object request, final int timeoutMillis) throws RemotingException, InterruptedException;


    Object invokeSync(final String address, final Object request, final RequestContext requestContext, final int timeoutMillis) throws RemotingException, InterruptedException;


    Object invokeSync(final Url url, final Object request, final int timeoutMillis) throws RemotingException, InterruptedException;

    Object invokeSync(final Url url, final Object request, final RequestContext requestContext, final int timeoutMillis) throws RemotingException, InterruptedException;


    Object invokeSync(final Channel channel, final Object request, final int timeoutMillis) throws RemotingException, InterruptedException;

    Object invokeSync(final Channel channel, final Object request, final RequestContext requestContext, final int timeoutMillis) throws RemotingException, InterruptedException;

    /**
     * Future invocation using a string address, address format example - 127.0.0.1:12200?key1=value1&key2=value2 <br>
     * You can get result use the returned {@link RpcResponseFuture}.
     * <p>
     * Notice:<br>
     * <ol>
     * <li><b>DO NOT modify the request object concurrently when this method is called.</b></li>
     * <li>When do invocation, use the string address to find a available channel, if none then create one.</li>
     *    <ul>
     *      <li>You can use {@link RpcConfigs#Channel_TIMEOUT_KEY} to specify channel timeout, time unit is milliseconds, e.g [127.0.0.1:12200?_CONNECTTIMEOUT=3000]
     *      <li>You can use {@link RpcConfigs#Channel_NUM_KEY} to specify channel number for each ip and port, e.g [127.0.0.1:12200?_channelNUM=30]
     *      <li>You can use {@link RpcConfigs#Channel_WARMUP_KEY} to specify whether need warmup all channels for the first time you call this method, e.g [127.0.0.1:12200?_channelWARMUP=false]
     *    </ul>
     * <li>You should use {@link #closeChannel(String addr)} to close it if you want.
     * </ol>
     */
    RpcResponseFuture invokeWithFuture(final String address, final Object request, final int timeoutMillis) throws RemotingException, InterruptedException;


    RpcResponseFuture invokeWithFuture(final String address, final Object request, final RequestContext requestContext, final int timeoutMillis) throws RemotingException, InterruptedException;


    RpcResponseFuture invokeWithFuture(final Url url, final Object request, final int timeoutMillis) throws RemotingException, InterruptedException;

    RpcResponseFuture invokeWithFuture(final Url url, final Object request, final RequestContext requestContext, final int timeoutMillis) throws RemotingException, InterruptedException;


    RpcResponseFuture invokeWithFuture(final Channel channel, final Object request, int timeoutMillis) throws RemotingException;


    RpcResponseFuture invokeWithFuture(final Channel channel, final Object request, final RequestContext requestContext, int timeoutMillis) throws RemotingException;

    /**
     * Callback invocation using a string address, address format example - 127.0.0.1:12200?key1=value1&key2=value2 <br>
     * You can specify an implementation of {@link Callback} to get the result.
     * <p>
     * Notice:<br>
     * <ol>
     * <li><b>DO NOT modify the request object concurrently when this method is called.</b></li>
     * <li>When do invocation, use the string address to find a available channel, if none then create one.</li>
     *    <ul>
     *      <li>You can use {@link RpcConfigs#Channel_TIMEOUT_KEY} to specify channel timeout, time unit is milliseconds, e.g [127.0.0.1:12200?_CONNECTTIMEOUT=3000]
     *      <li>You can use {@link RpcConfigs#Channel_NUM_KEY} to specify channel number for each ip and port, e.g [127.0.0.1:12200?_channelNUM=30]
     *      <li>You can use {@link RpcConfigs#Channel_WARMUP_KEY} to specify whether need warmup all channels for the first time you call this method, e.g [127.0.0.1:12200?_channelWARMUP=false]
     *    </ul>
     * <li>You should use {@link #closeChannel(String addr)} to close it if you want.
     * </ol>
     */
    void invokeWithCallback(final String addr, final Object request, final Callback callback, final int timeoutMillis) throws RemotingException, InterruptedException;

    void invokeWithCallback(final String addr, final Object request, final RequestContext requestContext, final Callback callback, final int timeoutMillis) throws RemotingException, InterruptedException;


    void invokeWithCallback(final Url url, final Object request, final Callback callback, final int timeoutMillis) throws RemotingException, InterruptedException;

    void invokeWithCallback(final Url url, final Object request, final RequestContext requestContext, final Callback callback, final int timeoutMillis) throws RemotingException, InterruptedException;


    void invokeWithCallback(final Channel channel, final Object request, final Callback callback, final int timeoutMillis) throws RemotingException;

    void invokeWithCallback(final Channel channel, final Object request, final RequestContext requestContext, final Callback callback, final int timeoutMillis) throws RemotingException;

    /**
     * Add processor to process channel event.
     */
    void addChannelEventProcessor(ChannelEventType type, ChannelEventProcessor processor);

    /**
     * Use UserProcessorRegisterHelper{@link UserProcessorRegisterHelper} to help register user processor for client side.
     */
    void registerUserProcessor(UserProcessor<?> processor);

    /**
     * Create a stand alone channel using ip and port. <br>
     * <p>
     * Notice:<br>
     * <li>Each time you call this method, will create a new channel.
     * <li> will not control this channel.
     * <li>You should use {@link #closeStandaloneChannel} to close it.
     *
     * @param ip             ip
     * @param port           port
     * @param connectTimeout timeout in millisecond
     */
    Channel createStandaloneChannel(String ip, int port, int connectTimeout) throws RemotingException;

    /**
     * Create a stand alone channel using address, address format example - 127.0.0.1:12200 <br>
     * <p>
     * Notice:<br>
     * <ol>
     * <li>Each time you can this method, will create a new channel.
     * <li> will not control this channel.
     * <li>You should use {@link #closeStandaloneChannel} to close it.
     * </ol>
     */
    Channel createStandaloneChannel(String address, int connectTimeout) throws RemotingException;


    void closeStandaloneChannel(Channel channel);

    /**
     * Get a channel using address, address format example - 127.0.0.1:12200?key1=value1&key2=value2 <br>
     * <p>
     * Notice:<br>
     * <ol>
     * <li>Get a channel, if none then create.</li>
     *    <ul>
     *      <li>You can use {@link RpcConfigs#Channel_TIMEOUT_KEY} to specify channel timeout, time unit is milliseconds, e.g [127.0.0.1:12200?_CONNECTTIMEOUT=3000]
     *      <li>You can use {@link RpcConfigs#Channel_NUM_KEY} to specify channel number for each ip and port, e.g [127.0.0.1:12200?_channelNUM=30]
     *      <li>You can use {@link RpcConfigs#Channel_WARMUP_KEY} to specify whether need warmup all channels for the first time you call this method, e.g [127.0.0.1:12200?_channelWARMUP=false]
     *    </ul>
     * <li> will control this channel in {@link Pool}
     * <li>You should use {@link #closeChannel(String addr)} to close it.
     * </ol>
     *
     * @param addr           target address
     * @param connectTimeout this is prior to url args {@link RpcConfigs#Channel_TIMEOUT_KEY}
     */
    Channel getChannel(String addr, int connectTimeout) throws RemotingException, InterruptedException;

    /**
     * Get a channel using a {@link Url}.<br>
     * <p>
     * Notice:
     * <ol>
     * <li>Get a channel, if none then create.
     * <li> will control this channel in {@link Pool}
     * <li>You should use {@link #closeChannel(Url url)} to close it.
     * </ol>
     *
     * @param url            target url
     * @param connectTimeout this is prior to url args {@link RpcConfigs#Channel_TIMEOUT_KEY}
     */
    Channel getChannel(Url url, int connectTimeout) throws RemotingException, InterruptedException;


    Map<String, List<Channel>> getAllManagedChannels();

    /**
     * check channel, the address format example - 127.0.0.1:12200?key1=value1&key2=value2
     *
     * @param address target address
     * @return true if and only if there is a channel, and the channel is active and writable;else return false
     */
    boolean checkChannel(String address);

    /**
     * check channel, the address format example - 127.0.0.1:12200?key1=value1&key2=value2
     * and if there is no channel, try create new one
     *
     * @param address        target address
     * @param createIfAbsent if need to create
     * @return true if and only if there is a channel, and the channel is active and writable, or can create a new one
     */
    boolean checkChannel(String address, boolean createIfAbsent);

    /**
     * check channel, the address format example - 127.0.0.1:12200?key1=value1&key2=value2
     * and if there is no channel, try create new one in asynchronous
     *
     * @param address        target address
     * @param createIfAbsent if need to create
     * @param createAsync    create channel asynchronous
     * @return true if and only if there is a channel, and the channel is active and writable
     */
    boolean checkChannel(String address, boolean createIfAbsent, boolean createAsync);


    void closeChannel(String address);


    void closeChannel(Url url);

    /**
     * Notice: this method takes no effect on a stand alone channel.
     */
    void enableConnHeartbeat(String address);


    void enableConnHeartbeat(Url url);


    void disableConnHeartbeat(String address);


    void disableConnHeartbeat(Url url);

    void enableReconnectSwitch();


    void disableReconnectSwith();


    boolean isReconnectSwitchOn();

    void enableChannelMonitorSwitch();


    void disableChannelMonitorSwitch();

    boolean isChannelMonitorSwitchOn();


    ChannelManager getChannelManager();

    void setChannelManager(ChannelManager channelManager);


}
